﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FakeItEasy;
using Sustainalytics.Portfolios.BL.Model;
using AutoFixture;
using Sustainalytics.DynamicsCRMGateway.Entities;
using System.Collections.Generic;
using Microsoft.Xrm.Client;
using System.Linq;
using Sustainalytics.Entities.ESGRatings;

namespace Sustainalytics.Universes.Synchronization.Tests
{
    [TestClass]
    public class SystemPortfolioManagerTest
    {
        SystemPortfolioInfo[] _portfolios;
        IUniverseInfo _universeInfo;
        const int accountNo = 10;
        const int companyNo = 12;
        const int companiesGroupNo = 5;

        [TestInitialize]
        public void TestInit()
        {
            var fixture = new Fixture();
            fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b => fixture.Behaviors.Remove(b));
            fixture.Behaviors.Add(new OmitOnRecursionBehavior());

            _universeInfo = A.Fake<IUniverseInfo>();

            FillAccounts(fixture);

            FillCompanies(fixture);

            FillCompaniesGroup(fixture);

            FillCompanyGroupAccountUniverse();

            FillAccountUniverse(fixture);

            FillCompaniesGroupAccountUniverse();

            FillReferenceCompanies(fixture);
            
            var portfolioManger = new SystemPortfolioManager(_universeInfo);
            _portfolios = portfolioManger.CreateSystemPortfolios();
        }

        private void FillReferenceCompanies(Fixture fixture)
        {
            var refUniv = new List<ReferenceUniverse>();
            fixture.RepeatCount = companiesGroupNo * companyNo;
            var compNames = fixture.CreateMany<string>().ToList();

            var groups = _universeInfo.CompaniesGroupSet.ToList();
            for (int i = 0; i < companiesGroupNo; i++)
            {
                if(i%2==0)
                {
                    var comps = new List<ReferenceUniverseCompany>();
                    for (int j = 0; j < companyNo; j++)
                        comps.Add(new ReferenceUniverseCompany() { CompanyId = Guid.NewGuid(), CompanyName = compNames[i * companyNo + j] });

                    refUniv.Add(new ReferenceUniverse(){Id = Guid.NewGuid(), Name = groups[i].Name, Companies=comps });
                }
            }
            _universeInfo.ReferenceUniverseSet = refUniv.ToArray();
        }

        private void FillCompaniesGroupAccountUniverse()
        {
            var cgauList = new List<CompaniesGroupAccountUniverseBridge>();
            foreach (var g in _universeInfo.CompaniesGroupSet)
            {
                foreach (var au in _universeInfo.AccountUniverseSet)
                {
                    cgauList.Add(new CompaniesGroupAccountUniverseBridge() { AccountUniverseId = au.AccountUniverseId, CompaniesGroupId = g.CompaniesGroupId });
                }
            }
            _universeInfo.CompaniesGroupAccountUniverseBridgeSet = cgauList;
        }

        private void FillAccountUniverse(Fixture fixture)
        {
            var auList = new List<AccountUniverse>();
            fixture.RepeatCount = accountNo;
            var ids = fixture.CreateMany<Guid>().ToList();
            var accs = _universeInfo.AccountSet.ToList();
            for (int i = 0; i < accountNo; i++)
            {
                var a = accs[i];
                auList.Add(new AccountUniverse() { AccountUniverseId = ids[i], AccountRef = new CrmEntityReference { Id = a.Id } });
            }
            _universeInfo.AccountUniverseSet = auList;
        }

        private void FillCompaniesGroup(Fixture fixture)
        {
            var cgList = new List<CompaniesGroup>();
            fixture.RepeatCount = companiesGroupNo;
            var names = fixture.CreateMany<string>().ToList();
            var ids = fixture.CreateMany<Guid>().ToList();
            for (int i = 0; i < companiesGroupNo; i++)
            {
                cgList.Add(new CompaniesGroup() { Id = ids[i], CompaniesGroupId = ids[i], Name = names[i], IsStandard = i%2==0 });
            }
            _universeInfo.CompaniesGroupSet = cgList;
        }

        private void FillCompanyGroupAccountUniverse()
        {
            var cgcList = new List<CompaniesGroupConstituent>();
            foreach (var g in _universeInfo.CompaniesGroupSet)
            {
                foreach (var c in _universeInfo.CompanySet)
                {
                    cgcList.Add(new CompaniesGroupConstituent() { CompaniesGroupRef = new CrmEntityReference { Id = g.Id }, CompanyRef = new CrmEntityReference { Id = c.Id } });
                }
            }
            _universeInfo.CompaniesGroupConstituentSet = cgcList;
        }

        private void FillAccounts(Fixture fixture)
        {
            fixture.RepeatCount = accountNo;
            var accIds = fixture.CreateMany<Guid>().ToList();
            var accNames = fixture.CreateMany<string>().ToList();

            var accounts = new List<Account>();
            for (int i = 0; i < accountNo; i++)
            {
                accounts.Add(new Account() { Id = accIds[i], AccountId = accIds[i], Name = accNames[i] });
            }
            _universeInfo.AccountSet = accounts;
        }

        private void FillCompanies(Fixture fixture)
        {
            fixture.RepeatCount = companyNo;
            var compIds = fixture.CreateMany<Guid>().ToList();
            var compNames = fixture.CreateMany<string>().ToList();
            var companies = new List<Company>();
            for (int i = 0; i < companyNo; i++)
            {
                companies.Add(new Company() { Id = compIds[i], CompanyId = compIds[i], Name = compNames[i] });
            }
            _universeInfo.CompanySet = companies;
        }

        [TestMethod]
        public void TestPortfolioNumber()
        {
            Assert.AreEqual(companiesGroupNo, _portfolios.Length);
        }

        [TestMethod]
        public void TestPortfolioAccounts()
        {
            _portfolios.ToList().ForEach(p => Assert.AreEqual(accountNo, p.AccountIds.Length));
        }

        [TestMethod]
        public void TestPortfolioCompanies()
        {
            _portfolios.ToList().ForEach(p => Assert.AreEqual(companyNo, p.CompanyIds.Length));
        }

        [TestMethod]
        public void TestPortfolioNames()
        {
            var names = _portfolios.ToList().Select(p => p.Name).ToList();
            CollectionAssert.AreEquivalent(_universeInfo.CompaniesGroupSet.Select(g => g.Name).ToList(), names);
        }
    }
}
